package com.sdp.right.utils;

import javax.crypto.Cipher;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;

public class RsaUtil {

	//签名算法名称
    private static final String RSA_KEY_ALGORITHM = "RSA";

    //标准签名算法名称
    private static final String RSA_SIGNATURE_ALGORITHM = "SHA1withRSA";
    private static final String RSA2_SIGNATURE_ALGORITHM = "SHA256withRSA";

    //RSA密钥长度,默认密钥长度是1024,密钥长度必须是64的倍数，在512到65536位之间,不管是RSA还是RSA2长度推荐使用2048
    private static final int KEY_SIZE = 2048;
    
    private static Map<String, String> keyMap = new HashMap<>();
    
    /**
     * 生成密钥对
     *
     * @return 返回包含公私钥的map
     */
    public static Map<String, String> generateKey() {
        KeyPairGenerator keygen;
        try {
            keygen = KeyPairGenerator.getInstance(RSA_KEY_ALGORITHM);
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("RSA初始化密钥出现错误,算法异常");
        }
        SecureRandom secrand = new SecureRandom();
        //初始化随机产生器
        secrand.setSeed("Alian".getBytes());
        //初始化密钥生成器
        keygen.initialize(KEY_SIZE, secrand);
        KeyPair keyPair = keygen.genKeyPair();
        //获取公钥并转成base64编码
        byte[] pub_key = keyPair.getPublic().getEncoded();
        String publicKeyStr = Base64.getEncoder().encodeToString(pub_key);
        //获取私钥并转成base64编码
        byte[] pri_key = keyPair.getPrivate().getEncoded();
        String privateKeyStr = Base64.getEncoder().encodeToString(pri_key);
        //创建一个Map返回结果
        Map<String, String> keyPairMap = new HashMap<>();
        keyPairMap.put("publicKeyStr", publicKeyStr);
        keyPairMap.put("privateKeyStr", privateKeyStr);
        keyMap = keyPairMap;
        return keyPairMap;
    }

    /**
     * 公钥加密(用于数据加密)
     *
     * @param data         加密前的字符串
     * @param publicKeyStr base64编码后的公钥
     * @return base64编码后的字符串
     * @throws Exception
     */
    public static String encryptByPublicKey(String data, String publicKeyStr) throws Exception {
        //Java原生base64解码
        byte[] pubKey = Base64.getDecoder().decode(publicKeyStr);
        //创建X509编码密钥规范
        X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(pubKey);
        //返回转换指定算法的KeyFactory对象
        KeyFactory keyFactory = KeyFactory.getInstance(RSA_KEY_ALGORITHM);
        //根据X509编码密钥规范产生公钥对象
        PublicKey publicKey = keyFactory.generatePublic(x509KeySpec);
        //根据转换的名称获取密码对象Cipher（转换的名称：算法/工作模式/填充模式）
        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
        //用公钥初始化此Cipher对象（加密模式）
        cipher.init(Cipher.ENCRYPT_MODE, publicKey);
        //对数据加密
        byte[] encrypt = cipher.doFinal(data.getBytes());
        //返回base64编码后的字符串
        return Base64.getEncoder().encodeToString(encrypt);
    }

    /**
     * 私钥解密(用于数据解密)
     *
     * @param data          解密前的字符串
     * @param privateKeyStr 私钥
     * @return 解密后的字符串
     * @throws Exception
     */
    public static String decryptByPrivateKey(String data, String privateKeyStr) throws Exception {
        //Java原生base64解码
        byte[] priKey = Base64.getDecoder().decode(privateKeyStr);
        //创建PKCS8编码密钥规范
        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(priKey);
        //返回转换指定算法的KeyFactory对象
        KeyFactory keyFactory = KeyFactory.getInstance(RSA_KEY_ALGORITHM);
        //根据PKCS8编码密钥规范产生私钥对象
        PrivateKey privateKey = keyFactory.generatePrivate(pkcs8KeySpec);
        //根据转换的名称获取密码对象Cipher（转换的名称：算法/工作模式/填充模式）
        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
        //用私钥初始化此Cipher对象（解密模式）
        cipher.init(Cipher.DECRYPT_MODE, privateKey);
        //对数据解密
        byte[] decrypt = cipher.doFinal(Base64.getDecoder().decode(data));
        //返回字符串
        return new String(decrypt);
    }

    /**
     * 私钥加密(用于数据签名)
     *
     * @param data          加密前的字符串
     * @param privateKeyStr base64编码后的私钥
     * @return base64编码后后的字符串
     * @throws Exception
     */
    public static String encryptByPrivateKey(String data, String privateKeyStr) throws Exception {
        //Java原生base64解码
        byte[] priKey = Base64.getDecoder().decode(privateKeyStr);
        //创建PKCS8编码密钥规范
        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(priKey);
        //返回转换指定算法的KeyFactory对象
        KeyFactory keyFactory = KeyFactory.getInstance(RSA_KEY_ALGORITHM);
        //根据PKCS8编码密钥规范产生私钥对象
        PrivateKey privateKey = keyFactory.generatePrivate(pkcs8KeySpec);
        //根据转换的名称获取密码对象Cipher（转换的名称：算法/工作模式/填充模式）
        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
        //用私钥初始化此Cipher对象（加密模式）
        cipher.init(Cipher.ENCRYPT_MODE, privateKey);
        //对数据加密
        byte[] encrypt = cipher.doFinal(data.getBytes());
        //返回base64编码后的字符串
        return Base64.getEncoder().encodeToString(encrypt);
    }

    /**
     * 公钥解密(用于数据验签)
     *
     * @param data         解密前的字符串
     * @param publicKeyStr base64编码后的公钥
     * @return 解密后的字符串
     * @throws Exception
     */
    public static String decryptByPublicKey(String data, String publicKeyStr) throws Exception {
        //Java原生base64解码
        byte[] pubKey = Base64.getDecoder().decode(publicKeyStr);
        //创建X509编码密钥规范
        X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(pubKey);
        //返回转换指定算法的KeyFactory对象
        KeyFactory keyFactory = KeyFactory.getInstance(RSA_KEY_ALGORITHM);
        //根据X509编码密钥规范产生公钥对象
        PublicKey publicKey = keyFactory.generatePublic(x509KeySpec);
        //根据转换的名称获取密码对象Cipher（转换的名称：算法/工作模式/填充模式）
        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
        //用公钥初始化此Cipher对象（解密模式）
        cipher.init(Cipher.DECRYPT_MODE, publicKey);
        //对数据解密
        byte[] decrypt = cipher.doFinal(Base64.getDecoder().decode(data));
        //返回字符串
        return new String(decrypt);
    }

    /**
     * RSA签名
     *
     * @param data     待签名数据
     * @param priKey   私钥
     * @param signType RSA或RSA2
     * @return 签名
     * @throws Exception
     */
    public static String sign(byte[] data, byte[] priKey, String signType) throws Exception {
        //创建PKCS8编码密钥规范
        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(priKey);
        //返回转换指定算法的KeyFactory对象
        KeyFactory keyFactory = KeyFactory.getInstance(RSA_KEY_ALGORITHM);
        //根据PKCS8编码密钥规范产生私钥对象
        PrivateKey privateKey = keyFactory.generatePrivate(pkcs8KeySpec);
        //标准签名算法名称(RSA还是RSA2)
        String algorithm = RSA_KEY_ALGORITHM.equals(signType) ? RSA_SIGNATURE_ALGORITHM : RSA2_SIGNATURE_ALGORITHM;
        //用指定算法产生签名对象Signature
        Signature signature = Signature.getInstance(algorithm);
        //用私钥初始化签名对象Signature
        signature.initSign(privateKey);
        //将待签名的数据传送给签名对象(须在初始化之后)
        signature.update(data);
        //返回签名结果字节数组
        byte[] sign = signature.sign();
        //返回Base64编码后的字符串
        return Base64.getEncoder().encodeToString(sign);
    }

    /**
     * RSA校验数字签名
     *
     * @param data     待校验数据
     * @param sign     数字签名
     * @param pubKey   公钥
     * @param signType RSA或RSA2
     * @return boolean 校验成功返回true，失败返回false
     */
    public static boolean verify(byte[] data, byte[] sign, byte[] pubKey, String signType) throws Exception {
        //返回转换指定算法的KeyFactory对象
        KeyFactory keyFactory = KeyFactory.getInstance(RSA_KEY_ALGORITHM);
        //创建X509编码密钥规范
        X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(pubKey);
        //根据X509编码密钥规范产生公钥对象
        PublicKey publicKey = keyFactory.generatePublic(x509KeySpec);
        //标准签名算法名称(RSA还是RSA2)
        String algorithm = RSA_KEY_ALGORITHM.equals(signType) ? RSA_SIGNATURE_ALGORITHM : RSA2_SIGNATURE_ALGORITHM;
        //用指定算法产生签名对象Signature
        Signature signature = Signature.getInstance(algorithm);
        //用公钥初始化签名对象,用于验证签名
        signature.initVerify(publicKey);
        //更新签名内容
        signature.update(data);
        //得到验证结果
        return signature.verify(sign);
    }

    

    public static String getpublicKey() {
		return keyMap.get("publicKeyStr");
	}
    
    public static String getprivateKey() {
		return keyMap.get("privateKeyStr");
	}


	public static void cleanKeyMap() {
		RsaUtil.keyMap.clear();
	}

	public static void main(String[] args) throws Exception {
    	Map<String, String> keyMap = RsaUtil.generateKey();
        String publicKeyStr = keyMap.get("publicKeyStr");
        String privateKeyStr = keyMap.get("privateKeyStr");
        System.out.println("-----------------生成的公钥和私钥------------------------------");
        System.out.println("获取到的公钥：" + publicKeyStr);
        System.out.println("获取到的私钥：" + privateKeyStr);
        // 待加密数据
//        String data = "报错问题解释：在Java中使用RSA解密时，如果遇到“字符过长”的错误，这通常意味着你试图解密的数据超过了RSA算法支持的最大块大小。RSA算法本身限制了它可以加密的数据块的大小，这个大小由密钥长度决定。对于一个2048位的密钥，可以加密的数据最大为256字节（大约2048位/8字节）。解决方法：使用较小的密钥，但这会降低安全性。对数据进行分块处理：将数据分成小块，每个块的大小不超过密钥允许的最大值。对每个块进行单独加密。接收方再对它们进行解密并重新组合。使用对称加密算法（如AES）加密数据，并仅使用RSA加密对称密钥。使用数据格式，如JSON，它有内置的支持来处理大量数据的分割和重组。";
        String data = "报错问题解释在Java中使用RSA解密时，如果遇到“字符过长”的错误，这通常意味着你试图解密的数据超过了RSA算法支持的最大块大小。RSA算法本身限制了它可以加密的数据块的大小，这个大小由密钥长度决定。对于一个2048位的密钥，可以加密的数据最大为256字节（大约2048位/8字节）。解决方法：使用较小的密钥，但这会降低安全性。对数据进行分块处理：将数据分成小块，每个块的大小不超过密钥允许的最大值。对每个块进行单独加密。接收方再对它们进行解密并重新组合。使用对称加密算法（如AES）加密数据，并仅使用RSA加密对称密钥。使用数据格式，如JSON，它有内置的支持来处理大量数据的分割和重组。";
    	
        
        // 公钥加密
        System.out.println("-----------------加密和解密------------------------------");
        System.out.println("待加密的数据：" + data);
        
        
        
        String encrypt = RsaUtil.encryptByPublicKey(data, publicKeyStr);
        System.out.println("加密后数据：" + encrypt);
        // 私钥解密
//        String decrypt = RsaUtil.decryptByPrivateKey("CaR8Jj3O504pO9/R5lKATasehI9FmAhYtfDtYe6jLaFMbEo96R/JnPZ7YWH1Jau29kfPW1MVuZrwLZw9jtzCwKoO6XPjPG7RzXWA9uORdgxQB2KEl52LJcDeiUZ6YuzzmuEolLQURSOWN5IWk+g7fzqAzd+cgRJTbsoanGy7kr9mVyBOt80SfvggW+sVZAZ1l+MjiVaWzH4E8PPsPlU2aWxNwylDmXuZfi+nal+/EphAVbpfBQOY5ry/UG+uMFDwbTYXluljwzjY8pUYXwC9gUL/P1MQPIVUaXuyRMjdoITVMH30N/+L/++IYOXIk/BSIq5vPQu6nr1k89DP8VNzeA==", 
//        		"MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCiExFOjVRuZJcGX2CXj4F1A/uPnEHg+4NozZSgjJfu3Rft2N7E6HNDbP9llBEcvHD3LRrQxVKYYNANSPr7JApyAqB5mOxNqM2xrRx42EfrjLhsOPw70RYeoTAgCIO6vej0rRYWheAhd5dLQbim5g1q67iEUQMzwVAT8FnwLs5fY0t0KqpfokiF+aryk6WWgtoOWVLnxcu1Hygh9CcGLBMhrGpgAtR//hKHwPM3ia+Ii+c7t9wYUFNPDRo1OuhHKxvNEmV2uVTvsu0eBkCPTX2ytwgR1kmZ4DmdTB6rIL5FZoPEN2H9YX0c0J94su199OPuPjBCVEW9DKWt6NNUJoDjAgMBAAECggEAJARgwDggHzgzZ7MiYAwAFOUBrnRnH3xFQ7EL6nHUAb0YaD7dopyVoNJEH2q8MsqdwBBB+Usgg8Rf99PUEqN7vsJerycQvGFa9LUnsqM9uFbZVROE2jYo2D2x2CyMOZ+ieV1Nn0u49zcAZDUCt/Q/3xbrNlxafdHj3cp38iw0qHWn5JD4R3cBuZTvvlgrkPYXPHPYXJQ6y5bDDonb5FaDfdZRNuEdY73i2VT//DeivgZ+F4fj2uuxdTxW8dYmgXBrjjHAni6c06H7LCg8LTdam8jojVhLFvTnmwZALi0F2AMjZA4CL3bloHKjJwO6qwbKZdJ+WIeFsPsVZeoeQPQ24QKBgQD79ALiCDuwHZitEG4b1CPm2WzA3BMA0yiU+RBdFiPFbUqq8b1fMOcGwJhHw6gfV75t1BY2tso0++on9WPM6j6VPIGrbVhfGRmnCCBQ1HM26hRBAUYVhqi6VAdxrJbatzAkqjT5fMLbPRWDxWI+nhWNSHVulqEVP7AhZybQqZsG9wKBgQCkrX2L3erCBWfJ3cn14G9CMNy8gXU2LfD5TD1kTPDAp+e0pviIsxWGIbikZnVft7TdEb9l6V8W7QOnrqFQPluZOKeHNBIW6jx8L9pmUWU2fKOLiuNxeBy79xz24gqydfAkVhmkPYZJJ19cN2CvLXgrC13zv7SgfDkJ2TkK+qe+dQKBgCWz0wad8sic9S7YqZFygljWKmcFx7Y04vq/9onneTze6PCBsjPfboLdKSx8A9mE2NM4jIZtff7Fv7lxCBku6GSOpFzRxQqeB++t0KAn2oHaL2O23BT2jKMtVdvV1tEt3n9uFPVMjSAfmOuaCuSR5Wm58RC2a65xVFr7Q5yv9KnrAoGAImtorE1L/hWnJdhHmC660njoCjZ8y0A698BprIcwYvQ1R9sGY3wa6ZguwZ3wtnrtQK+u85wdlesgMnIboWwHNTVUPrwxc7ytu788xNoKb6MJg+cZtuPGDbuOwENqvPZNpSqh3WRGpw+lrN6eRL8aONT9ZC8Tj9+nBFn0xEizuukCgYA4sBJOJvdwxLXGkf87WWXjgpaVudLwfWquOy67hoM2Mn9Ilf3Wbl99rFJLq+QcHv9jllIza56E9ooVLYnMiVDzZNRceewYhCIExRppY7/sn/qeJHsymGU2S749zxNbwii6N1XmWncrr9kshP2sB3Cn5eUlr5YE8mQ++WJLD8Rhnw==");
//        System.out.println("解密后数据：" + decrypt);
	}
}
